#include "ServerHttpProxy.h"
#include "ServerCase.h"
#include "ServerHook.h"

static int get_query_string(const char* data, char* buf, size_t* buflen){
	const char* p = strstr(data, " ");
	if (p == NULL)
		return -1;
	p++;
	const char* e = strstr(p, " ");
	if (e == NULL)
		return -1;
	const char* s = strstr(p, "?");
	if (!s || s > e){
		return -2;
	}
	s++;
	size_t len = e - s;
	if (len >= *buflen)
		return -1;
	memcpy(buf, s++, len);
	*buflen = len;
	return 0;
}

int do_http_cors_proxy(HSOCKET hsock, ServerProtocol* proto, const char* data, int len){
	proto->peerType = PEER_HTTP_PROXY;
	char query[1024] = {0x0};
	size_t query_size = sizeof(query);
	get_query_string(data, query, &query_size);
	urldecode((unsigned char*)query, (unsigned char*)query);
	query_size = strlen(query);
	PROTOCOL protocol = TCP_PROTOCOL;
	char host[64] = {0x0};
	int port = 80;
	char uri[1024] = {0x0};
	size_t uri_size = 0;
	const char* p;
	p = strstr(query, "https://");
	if (p){
		protocol = SSL_PROTOCOL;
		port = 443;
		p += 8;
	}
	else{
		p = strstr(query, "http://");
		if(p){
			p += 7;
		}else{
			p = query;
		}
	}
	const char* m = strchr(p, '/');
	if (m){
		memcpy(host, p, m - p);
		uri_size = query_size - (m - query);
		memcpy(uri, m, uri_size);
	}
	else{
		memcpy(host, p, query_size - (p - query));
		memcpy(uri, "/", sizeof("/"));
		uri_size = 1;
	}
	char* x = strchr(host, ':');
	if (x) {
		port = atoi(x + 1);
		*x = 0x0;
	}

	h_HTTP_PROXY http_proxy = (h_HTTP_PROXY)malloc(sizeof(t_http_proxy));
	if (!http_proxy) return -1;
	memset(http_proxy, 0x0, sizeof(t_http_proxy));
	proto->http_proxy = http_proxy;
	http_proxy->data = (char*)malloc(len);
	if (!http_proxy->data) return -1;
	size_t offset = 0;
	p = strchr(data, ' ');
	if (!p) return -1;
	p++;
	memcpy(http_proxy->data, data, p - data);
	offset = p - data;
	memcpy(http_proxy->data + offset, uri, uri_size);
	offset += uri_size;
	p = strchr(p, ' ');
	if (!p) return -1;
	m = strstr(p, "\r\nHost:");
	size_t n = 0;
	if (!m){
		n = len - (p - data);
		memcpy(http_proxy->data + offset, p, n);
		offset += n;
	}
	else{
		n = m - p;
		memcpy(http_proxy->data + offset, p, n);
		offset += n;

		offset += snprintf(http_proxy->data + offset, len - offset, "\r\nHost: %s:%d", host, port);
		p = strstr(m+7, "\r\n");
		if (!p) return -1;
		n = len - (p - data);
		memcpy(http_proxy->data + offset, p, n);
		offset += n;
	}
	//*(http_proxy->data + offset) = 0x0;
	http_proxy->len = (int)offset;

	char ip[40] = { 0x0 };
	GetHostByName(host, ip, sizeof(ip));
	snprintf(http_proxy->host, sizeof(http_proxy->host), "%s", host);
	http_proxy->port = port;

	http_proxy->proxySock =  HsocketConnect(proto, ip, port, protocol);
	http_proxy->proxyStat = PROXY_CONNECTING;
	http_proxy->proxy_type = CORS_PROXY;
	return len;
}

int do_http_proxy(HSOCKET hsock, ServerProtocol* proto, char* url, const char* data, int len) {
	proto->peerType = PEER_HTTP_PROXY;
	h_HTTP_PROXY http_proxy = (h_HTTP_PROXY)malloc(sizeof(t_http_proxy));
	if (!http_proxy) return -1;
	memset(http_proxy, 0x0, sizeof(t_http_proxy));
	proto->http_proxy = http_proxy;
	http_proxy->data = (char*)malloc(len);
	if (!http_proxy->data) return -1;
	memcpy(http_proxy->data, data, len);
	char* s = strstr(http_proxy->data, "http://");
	char* e = strchr(s + 7, '/');
	int movelen = int(len - (e - http_proxy->data));
	memmove(s, e, movelen);
	*(s + movelen) = 0x0;
	http_proxy->len = int(s - http_proxy->data + movelen);

	char* uri = strchr(url, '/');
	if (!uri) return -1;
	*uri = 0x0;
	int port = 80;
	char* p = strchr(url, ':');
	if (p) {
		port = atoi(p + 1);
		*p = 0x0;
	}
	char ip[40] = { 0x0 };
	GetHostByName(url, ip, sizeof(ip));

	snprintf(http_proxy->host, sizeof(http_proxy->host), "%s", url);
	http_proxy->port = port;

	http_proxy->proxySock = HsocketConnect(proto, ip, port, TCP_PROTOCOL);
	http_proxy->proxyStat = PROXY_CONNECTING;
	http_proxy->proxy_type = HTTP_PROXY;
	return len;
}

int do_https_proxy(HSOCKET hsock, ServerProtocol* proto, char* url, const char* data, int len) {
	proto->peerType = PEER_HTTP_PROXY;
	h_HTTP_PROXY http_proxy = (h_HTTP_PROXY)malloc(sizeof(t_http_proxy));
	if (!http_proxy) return -1;
	memset(http_proxy, 0x0, sizeof(t_http_proxy));
	proto->http_proxy = http_proxy;

	int port = 80;
	char* p = strchr(url, ':');
	if (p) {
		port = atoi(p + 1);
		*p = 0x0;
	}
	char ip[40] = { 0x0 };
	GetHostByName(url, ip, sizeof(ip));

	snprintf(http_proxy->host, sizeof(http_proxy->host), "%s", url);
	http_proxy->port = port;
	
	char portstr[8] = { 0x0 };
	int portlen = snprintf(portstr, sizeof(portstr), "%d", port);
	const char* ports = config_get_string_value("server", "proxy_ssl_ports", NULL);
	if (ports) {
		const char* p_port = ports;
		const char* split = NULL;
		while (1) {
			split = strchr(p_port, ',');
			if (split) {
				if (split - p_port == portlen && strncmp(p_port, portstr, portlen) == 0) {
					http_proxy->ssl_conn = true;
					break;
				}
				p_port = split + 1;
			}
			else {
				if (strncmp(p_port, portstr, portlen) == 0) http_proxy->ssl_conn = true;
				break;
			}
		}
	}
	if (http_proxy->ssl_conn) {
		http_proxy->proxySock = HsocketConnect(proto, ip, port, SSL_PROTOCOL);
	}
	else{
		http_proxy->proxySock = HsocketConnect(proto, ip, port, TCP_PROTOCOL);
	}
	http_proxy->proxyStat = PROXY_CONNECTING;
	http_proxy->proxy_type = HTTPS_PROXY;
	return len;
}

void http_proxy_connection_made(HSOCKET hsock, ServerProtocol* proto) {
	if (proto->http_proxy->proxySock == hsock) {
		h_HTTP_PROXY http_proxy = proto->http_proxy;
		http_proxy->proxyStat = PROXY_CONNECTED;
		http_proxy->sessionid = proto->initSock->fd;
		http_proxy->protocol = http_proxy->proxySock->protocol;

		if (http_proxy->proxy_type == HTTP_PROXY) {
			if (proxy_hook && server_hook) {
				server_hook->proxy_connect_open_hook("http", http_proxy->host, proto->initSock, http_proxy->proxySock, TCP_PROTOCOL);
			}

			int hook_ret = 0;
			if (proxy_hook && server_hook) {
				hook_ret = server_hook->proxy_connect_send_hook("http", proto->initSock, http_proxy->proxySock, TCP_PROTOCOL, http_proxy->data, http_proxy->len);
			}

			if (hook_ret == 0) HsocketSend(hsock, http_proxy->data, http_proxy->len);
			if (Proxy_record == true){
				insert_msg_to_record_list(http_proxy->host, http_proxy->port, http_proxy->sessionid, 1, HTTP_PROTOCOL, http_proxy->data, http_proxy->len);
			}
		}
		else if(http_proxy->proxy_type == HTTPS_PROXY){
			HsocketSend(proto->initSock, "HTTP/1.1 200 Connection Established\r\n\r\n", 39);
			if (http_proxy->ssl_conn) {
				HsocketSSLCreate(proto->initSock, SSL_SERVER, 0, NULL, user_crt, pri_key);
			}

			char peerip[40] = { 0x0 };
			int peerport = 0;
			HsocketPeerAddr(proto->initSock, peerip, sizeof(peerip), &peerport);

			char proxy_addr[256] = { 0x0 };
			snprintf(proxy_addr, sizeof(proxy_addr), "从%s:%d  到%s:%d", peerip, peerport, http_proxy->host, http_proxy->port);
			ProxyAddr->insert(std::make_pair(proto, proxy_addr));

			if (proxy_hook && server_hook) {
				server_hook->proxy_connect_open_hook("https", http_proxy->host, proto->initSock, http_proxy->proxySock, TCP_PROTOCOL);
			}

			if (Proxy_record)
				insert_msg_to_record_list(http_proxy->host, http_proxy->port, http_proxy->sessionid, 0, http_proxy->protocol, NULL, 0);
		}
		else if(http_proxy->proxy_type == CORS_PROXY){
			HsocketSend(hsock, http_proxy->data, http_proxy->len);
		}
	}
}

void http_proxy_connection_faile(HSOCKET hsock, ServerProtocol* proto) {
	h_HTTP_PROXY http_proxy = proto->http_proxy;
	if (hsock == http_proxy->proxySock){  //服务器连接失败
		if (proxy_hook && server_hook) {
			server_hook->proxy_connect_faile_hook(http_proxy->data? "http": "https", http_proxy->host, proto->initSock, http_proxy->proxySock, TCP_PROTOCOL);
		}
		http_proxy->proxySock = NULL;
		if (proto->initSock) HsocketClose(proto->initSock);
	}
	else if(hsock == proto->initSock) {  //客户端升级ssl连接失败
		proto->initSock = NULL;
		if (http_proxy->proxySock) HsocketClose(http_proxy->proxySock);
	}
}

void http_proxy_connection_closed(HSOCKET hsock, ServerProtocol* proto) {
	h_HTTP_PROXY http_proxy = proto->http_proxy;
	if (proxy_hook && server_hook && proto->initSock && http_proxy->proxySock) {
		server_hook->proxy_connect_close_hook( http_proxy->data? "http": "https", proto->initSock, http_proxy->proxySock, TCP_PROTOCOL);
	}
	if (hsock == proto->initSock) {
		ProxyAddr->erase(proto);
		proto->initSock = NULL;
		if (http_proxy->proxySock) HsocketClose(http_proxy->proxySock);
	}
	else if (hsock == http_proxy->proxySock) {
		http_proxy->proxySock = NULL;
		if (proto->initSock) HsocketClose(proto->initSock);
	}

	if (Proxy_record && http_proxy->proxy_type == HTTPS_PROXY && http_proxy->proxyStat == PROXY_CONNECTED && proto->initSock == NULL && http_proxy->proxySock == NULL ) {
		insert_msg_to_record_list(http_proxy->host, http_proxy->port, http_proxy->sessionid, 2, http_proxy->protocol, NULL, 0);
	}

	if (proto->initSock == NULL && http_proxy->proxySock == NULL && http_proxy->data) {
		free(http_proxy->data);
		http_proxy->data = NULL;
	}
}

void http_proxy_server_connection_recved(HSOCKET hsock, ServerProtocol* proto, const char* data, int len) {
	h_HTTP_PROXY http_proxy = proto->http_proxy;
	int hook_ret = 0;
	if (proxy_hook && server_hook) {
		hook_ret = server_hook->proxy_connect_recv_hook(http_proxy->data ? "http": "https", proto->initSock, http_proxy->proxySock, TCP_PROTOCOL, data, len);
	}
	if (hook_ret == 0) HsocketSend(proto->initSock, data, len);
}

int http_proxy_client_connection_recved(HSOCKET hsock, ServerProtocol* proto, const char* data, int len) {
	h_HTTP_PROXY http_proxy = proto->http_proxy;
	int hook_ret = 0;
	if (proxy_hook && server_hook) {
		hook_ret = server_hook->proxy_connect_send_hook(http_proxy->data ? "http": "https", proto->initSock, http_proxy->proxySock, TCP_PROTOCOL, data, len);
	}
	if (hook_ret == 0) HsocketSend(http_proxy->proxySock, data, len);
	if (Proxy_record)
		insert_msg_to_record_list(http_proxy->host, http_proxy->port, http_proxy->sessionid, 1, http_proxy->protocol, data, len);
	return len;
}

